#include "matrix_mem.h"
#include "file.h"
#include "fileStructs.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define PARTICION 99000
#define REGPARTICION 512492500
#define REG_OCUPADO 0
#define REG_LIBRE 1

unsigned long posicion(unsigned i, unsigned j);
int validar(unsigned i, unsigned j);
void invertir (unsigned *i, unsigned *j);
int validarNombre(const char* nombre);

void invertir (unsigned *i, unsigned *j) {
	int aux = *j;
	*j = *i;
	*i = aux;
}

unsigned long posicion(unsigned i, unsigned j) {
	unsigned long aux = (unsigned long) ( i * (i - 1) ) / 2;
	return aux + j;
}

int validar(unsigned i, unsigned j) {
	if (i == j) return -1;
	return 0;
}

int M_CREATE(const char *nombre_fisico, unsigned tamReg, unsigned maxFilCol) {
	int err;
	if (maxFilCol < 2) return RES_ERROR;
	if (maxFilCol < PARTICION)
		return R_CREATE(nombre_fisico, tamReg, posicion(maxFilCol - 1, maxFilCol - 1));

	err = R_CREATE(nombre_fisico, tamReg, REGPARTICION);

	char *nombre = (char*) malloc(strlen(nombre_fisico) + 1);

	unsigned long maxReg = posicion(maxFilCol - 1, maxFilCol - 1) - REGPARTICION;

	strcpy(nombre, nombre_fisico);
	strcat(nombre, "0");
	err = R_CREATE(nombre, tamReg, maxReg);

	free(nombre);
	return err;
}

mHandler *M_OPEN (const char* nombre_fisico, int modo) {
	mHandler *matHandler = (mHandler*) malloc(sizeof(mHandler));
	if (!matHandler) return NULL;

	if ( !(matHandler->handler[0] = R_OPEN(nombre_fisico, modo)) ) {
		free(matHandler);
		return NULL;
	}

	matHandler->regMax = matHandler->handler[0]->maxReg;
	matHandler->handler[1] = NULL;

	if (matHandler->handler[0]->maxReg == REGPARTICION) {
		char *nombre = (char*) malloc(strlen(nombre_fisico) + 1);
		strcpy(nombre, nombre_fisico);
		strcat(nombre, "0");

		matHandler->handler[1] = R_OPEN(nombre, modo);

		free(nombre);
	}

	if (matHandler->handler[1])
		matHandler->regMax += matHandler->handler[1]->maxReg;

	R_CLOSE(matHandler->handler[0]);

	if (matHandler->handler[1])
		R_CLOSE(matHandler->handler[1]);

	matHandler->matrix = malloc( sizeof(regMat)*matHandler->regMax );
	memset (matHandler->matrix, 0, sizeof(regMat)*matHandler->regMax);

	return matHandler;
}

unsigned posicionar_mem(unsigned long maxReg, unsigned tamReg) {
	return 2 * sizeof(unsigned) + sizeof(char) * maxReg;
}

int M_CLOSE (mHandler *matHandler) {
	if (!matHandler) return RES_ERROR;

	char *nombre_fisico = "/media/Compartido/matrix.dat";//TODO nombre fisico
	//Abro los archivos:
	if ( !(matHandler->handler[0] = R_OPEN(nombre_fisico, READ_WRITE)) ) {
		free(matHandler);
		return RES_ERROR;
	}

	matHandler->regMax = matHandler->handler[0]->maxReg;
	matHandler->handler[1] = NULL;

	if (matHandler->handler[0]->maxReg == REGPARTICION) {
		char *nombre = (char*) malloc(strlen(nombre_fisico) + 1);
		strcpy(nombre, nombre_fisico);
		strcat(nombre, "0");

		matHandler->handler[1] = R_OPEN(nombre, READ_WRITE);

		free(nombre);
	}

	if (matHandler->handler[1])
		matHandler->regMax += matHandler->handler[1]->maxReg;
	//Fin de apertura.

	//Almaceno:
	int i = 0;
	fseek(matHandler->handler[0]->handler, posicionar_mem(matHandler->handler[0]->maxReg, matHandler->handler[0]->tamReg), SEEK_SET);
	if (fwrite(matHandler->matrix, matHandler->handler[0]->tamReg * matHandler->handler[0]->maxReg, 1, matHandler->handler[0]->handler) < 1) return RES_ERROR;
	for ( i = 0 ; i <= matHandler->handler[0]->maxReg; i++) {
		matHandler->handler[0]->ocupacion[i] = REG_OCUPADO;
	}
	if ( matHandler->regMax > REGPARTICION ) {
		fseek(matHandler->handler[1]->handler, posicionar_mem(matHandler->handler[1]->maxReg, matHandler->handler[1]->tamReg), SEEK_SET);
		if (fwrite(&(matHandler->matrix[matHandler->handler[0]->maxReg]), matHandler->handler[1]->tamReg * matHandler->handler[1]->maxReg, 1, matHandler->handler[1]->handler) < 1) return RES_ERROR;
		for ( i = 0 ; i <= matHandler->handler[1]->maxReg; i++) {
			matHandler->handler[1]->ocupacion[i] = REG_OCUPADO;
		}
	}
	//Fin de almacenamiento

	//Cierro:

	R_CLOSE(matHandler->handler[0]);

	if (matHandler->handler[1])
		R_CLOSE(matHandler->handler[1]);

	free( matHandler->matrix );
	free( matHandler );

	return RES_OK;
}

int M_READ (mHandler *handler, unsigned posFil, unsigned posCol, void* reg) {
//	if (!handler) return RES_ERROR;
//	if (validar(posFil,posCol)) return RES_ERROR;
//	if (posFil < posCol) invertir(&posFil, &posCol);
//	unsigned long pos = posicion(posFil, posCol);
//	if (pos <= handler->regMax) {
//		if (pos < REGPARTICION)
//			return R_READ(handler->handler[0], pos, reg);
//
//		return R_READ(handler->handler[1], pos - REGPARTICION, reg);
//	}
	return RES_ERROR;
}

bool M_EXISTE_REG (mHandler *handler, unsigned posFil, unsigned posCol ) {
	if (posFil < posCol) invertir(&posFil, &posCol);
	unsigned long pos = posicion(posFil, posCol);

	if ( handler->matrix[pos].dist == 0 && handler->matrix[pos].idPel == 0 && handler->matrix[pos].idPer == 0 ) {
		return false;
	}
	return true;
}

int M_WRITE (mHandler *handler, unsigned posFil, unsigned posCol, const void* reg) {
	if (!handler) return RES_ERROR;
	if (validar(posFil,posCol)) return RES_ERROR;
	if (posFil < posCol) invertir(&posFil, &posCol);

	if ( M_EXISTE_REG(handler, posFil, posCol) )
		return RES_EXISTE;

	unsigned long pos = posicion(posFil, posCol);
	if (pos <= handler->regMax) {
		memcpy( &(handler->matrix[pos]), reg, sizeof(regMat) );
		return RES_OK;
	}
	return RES_ERROR;
}

int M_UPDATE (mHandler *handler, unsigned posFil, unsigned posCol, const void* reg) {
//	if (!handler) return RES_ERROR;
//	if (validar(posFil,posCol)) return RES_ERROR;
//	if (posFil < posCol) invertir(&posFil, &posCol);
//	unsigned long pos = posicion(posFil, posCol);
//	if (pos <= handler->regMax) {
//		if (pos < REGPARTICION)
//			return R_UPDATE(handler->handler[0], pos, reg);
//
//		return R_UPDATE(handler->handler[1], pos - REGPARTICION, reg);
//	}
	return RES_ERROR;
}

int M_DELETE (mHandler *handler, unsigned posFil, unsigned posCol) {
//	if (!handler) return RES_ERROR;
//	if (validar(posFil,posCol)) return RES_ERROR;
//	if (posFil < posCol) invertir(&posFil, &posCol);
//	unsigned long pos = posicion(posFil, posCol);
//	if (pos <= handler->regMax) {
//		if (pos < REGPARTICION)
//			return R_DELETE(handler->handler[0], pos);
//
//		return R_DELETE(handler->handler[1], pos - REGPARTICION);
//	}
	return RES_ERROR;
}

int M_DESTROY(const char* nombre_fisico) {

	int err = R_DESTROY(nombre_fisico);

	char *nombre = (char*) malloc(strlen(nombre_fisico) + 1);
	strcpy(nombre, nombre_fisico);
	strcat(nombre, "0");
	R_DESTROY(nombre);

	return err;
}
